2023-12-29 14:24:08 +00:00
|
|
|
import json
|
|
|
|
import os
|
|
|
|
import subprocess
|
2023-12-30 10:54:19 +00:00
|
|
|
import tempfile
|
|
|
|
import tarfile
|
|
|
|
import shutil
|
|
|
|
import urllib.request
|
2023-12-29 14:24:08 +00:00
|
|
|
from selfprivacy_api.jobs import JobStatus, Jobs
|
|
|
|
|
|
|
|
from selfprivacy_api.migrations.migration import Migration
|
|
|
|
|
|
|
|
|
|
|
|
class MigrateToFlakes(Migration):
|
|
|
|
"""Migrate to selfprivacy Nix channel.
|
|
|
|
For some reason NixOS 22.11 servers initialized with the nixos channel instead of selfprivacy.
|
|
|
|
This stops us from upgrading to NixOS 23.05
|
|
|
|
"""
|
|
|
|
|
|
|
|
def get_migration_name(self):
|
|
|
|
return "migrate_to_flakes"
|
|
|
|
|
|
|
|
def get_migration_description(self):
|
|
|
|
return "Migrate to selfprivacy Nix flakes."
|
|
|
|
|
|
|
|
def is_migration_needed(self):
|
2024-01-08 19:22:35 +00:00
|
|
|
# Check if there is at least 5 GiB of free space in the root partition
|
|
|
|
# This is needed to download the new NixOS configuration
|
|
|
|
statvfs = os.statvfs("/")
|
|
|
|
free_space = statvfs.f_frsize * statvfs.f_bavail
|
|
|
|
if free_space < 5 * 1024 * 1024 * 1024:
|
2024-01-08 19:27:02 +00:00
|
|
|
print("===================================")
|
|
|
|
print("NOT ENOUGH FREE SPACE FOR MIGRATION")
|
|
|
|
print("===================================")
|
2024-01-08 19:22:35 +00:00
|
|
|
Jobs.add(
|
|
|
|
name="NixOS upgrade to 23.11",
|
|
|
|
type_id="migrations.migrate_to_flakes",
|
|
|
|
status=JobStatus.ERROR,
|
|
|
|
status_text="Not enough free space in the root partition.",
|
|
|
|
description="Migration to the modular SelfPrivacy system",
|
|
|
|
)
|
|
|
|
return False
|
|
|
|
|
2023-12-29 14:24:08 +00:00
|
|
|
return True
|
|
|
|
|
|
|
|
def migrate(self):
|
|
|
|
# Change the channel and update them.
|
|
|
|
current_working_directory = os.getcwd()
|
|
|
|
try:
|
|
|
|
print("===========================")
|
|
|
|
print("STARTING MIGRATION TO 23.11")
|
|
|
|
print("===========================")
|
|
|
|
os.chdir("/")
|
|
|
|
|
|
|
|
print("Disabling automatic upgrades to prevent conflicts.")
|
|
|
|
subprocess.check_output(
|
|
|
|
[
|
|
|
|
"systemctl",
|
|
|
|
"stop",
|
|
|
|
"nixos-upgrade.service",
|
|
|
|
"nixos-upgrade.timer",
|
|
|
|
]
|
|
|
|
)
|
|
|
|
print("Disabled automatic upgrades.")
|
|
|
|
|
|
|
|
print("Reading the userdata file.")
|
|
|
|
userdata_file = open(
|
|
|
|
"/etc/nixos/userdata/userdata.json", "r", encoding="utf-8"
|
|
|
|
)
|
|
|
|
userdata: dict = json.load(userdata_file)
|
|
|
|
userdata_file.close()
|
|
|
|
print("Read file. Validating contents...")
|
2023-12-30 10:54:19 +00:00
|
|
|
assert userdata["dns"]["provider"] is not None
|
|
|
|
assert userdata["dns"]["apiKey"] is not None
|
|
|
|
assert userdata["useBinds"] is True
|
|
|
|
assert userdata["username"] is not None
|
2023-12-29 14:24:08 +00:00
|
|
|
assert userdata["username"] != ""
|
|
|
|
print("Userdata file is probably fine.")
|
|
|
|
|
|
|
|
print(
|
|
|
|
"Moving old NixOS configuration from /etc/nixos to /etc/nixos.pre-flakes"
|
|
|
|
)
|
|
|
|
subprocess.check_output(
|
|
|
|
[
|
|
|
|
"mv",
|
|
|
|
"-v",
|
|
|
|
"/etc/nixos",
|
|
|
|
"/etc/nixos.pre-flakes",
|
|
|
|
]
|
|
|
|
)
|
|
|
|
assert os.path.exists("/etc/nixos.pre-flakes/userdata/userdata.json")
|
|
|
|
print("Moved")
|
|
|
|
|
|
|
|
print("Downloading the template of the new NixOS configuration")
|
2023-12-30 10:54:19 +00:00
|
|
|
|
|
|
|
archive_url = "https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-template/archive/master.tar.gz"
|
|
|
|
temp_dir = tempfile.mkdtemp()
|
|
|
|
|
|
|
|
try:
|
|
|
|
archive_path = os.path.join(temp_dir, "archive.tar.gz")
|
|
|
|
urllib.request.urlretrieve(archive_url, archive_path)
|
|
|
|
|
|
|
|
with tarfile.open(archive_path, "r:gz") as tar:
|
2023-12-31 15:03:09 +00:00
|
|
|
tar.extractall(path=temp_dir)
|
2023-12-31 15:29:59 +00:00
|
|
|
extracted_folder = os.path.join(
|
|
|
|
temp_dir, "selfprivacy-nixos-template"
|
|
|
|
)
|
2023-12-31 15:06:00 +00:00
|
|
|
shutil.copytree(extracted_folder, "/etc/nixos")
|
2023-12-30 10:54:19 +00:00
|
|
|
|
|
|
|
finally:
|
|
|
|
shutil.rmtree(temp_dir)
|
2023-12-29 14:24:08 +00:00
|
|
|
print("Downloaded")
|
|
|
|
|
|
|
|
print("Copying hardware-configuration.nix")
|
2023-12-31 15:14:50 +00:00
|
|
|
hardware_config_path = "/etc/nixos.pre-flakes/hardware-configuration.nix"
|
|
|
|
new_hardware_config_path = "/etc/nixos/hardware-configuration.nix"
|
|
|
|
|
|
|
|
with open(hardware_config_path, "r") as hardware_config_file:
|
|
|
|
hardware_config_lines = hardware_config_file.readlines()
|
|
|
|
|
|
|
|
# Check if the file contains the line with "./networking.nix" substring
|
2023-12-31 15:29:59 +00:00
|
|
|
hardware_config_lines = [
|
|
|
|
line for line in hardware_config_lines if "./networking.nix" not in line
|
|
|
|
]
|
2023-12-31 15:14:50 +00:00
|
|
|
|
|
|
|
with open(new_hardware_config_path, "w") as new_hardware_config_file:
|
|
|
|
new_hardware_config_file.writelines(hardware_config_lines)
|
2023-12-29 14:24:08 +00:00
|
|
|
print("Copied")
|
|
|
|
|
|
|
|
print("Checking if /etc/nixos.pre-flakes/networking.nix exists")
|
|
|
|
if os.path.exists("/etc/nixos.pre-flakes/networking.nix"):
|
|
|
|
print("Transforming networking.nix to /etc/nixos/deployment.nix")
|
2023-12-30 10:54:19 +00:00
|
|
|
deployment_contents = '{ lib, ... }: {\n system.stateVersion = lib.mkDefault "23.11";\n nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";\n'
|
2023-12-30 10:54:41 +00:00
|
|
|
with open(
|
|
|
|
"/etc/nixos.pre-flakes/networking.nix", "r"
|
|
|
|
) as networking_file:
|
2023-12-30 10:54:19 +00:00
|
|
|
networking_contents = networking_file.read().splitlines(True)[1:]
|
|
|
|
deployment_contents += "\n" + "".join(networking_contents)
|
|
|
|
with open("/etc/nixos/deployment.nix", "w") as file:
|
|
|
|
file.write(deployment_contents)
|
2023-12-29 14:24:08 +00:00
|
|
|
else:
|
|
|
|
print("Generating /etc/nixos/deployment.nix")
|
|
|
|
deployment_contents = '{ lib, ... }: {\n system.stateVersion = lib.mkDefault "23.11";\n nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";\n}'
|
|
|
|
with open("/etc/nixos/deployment.nix", "w") as file:
|
|
|
|
file.write(deployment_contents)
|
|
|
|
|
|
|
|
print("Generated.")
|
|
|
|
|
|
|
|
print("Generating the new userdata file.")
|
|
|
|
new_userdata = {
|
|
|
|
"dns": {
|
|
|
|
"provider": userdata.get("dns", {}).get("provider", "CLOUDFLARE"),
|
2023-12-31 15:32:37 +00:00
|
|
|
"useStagingACME": False,
|
2023-12-29 14:24:08 +00:00
|
|
|
},
|
|
|
|
"server": {
|
|
|
|
"provider": userdata.get("server", {}).get("provider", "HETZNER")
|
|
|
|
},
|
|
|
|
"domain": userdata.get("domain"),
|
|
|
|
"hashedMasterPassword": userdata.get("hashedMasterPassword"),
|
|
|
|
"hostname": userdata.get("hostname"),
|
|
|
|
"timezone": userdata.get("timezone", "Etc/UTC"),
|
|
|
|
"username": userdata.get("username"),
|
|
|
|
"useBinds": True,
|
|
|
|
"sshKeys": userdata.get("sshKeys", []),
|
|
|
|
"users": userdata.get("users", []),
|
|
|
|
"autoUpgrade": {
|
|
|
|
"enable": userdata.get("autoUpgrade", {}).get("enable", True),
|
|
|
|
"allowReboot": userdata.get("autoUpgrade", {}).get(
|
|
|
|
"allowReboot", False
|
|
|
|
),
|
|
|
|
},
|
|
|
|
"modules": {
|
|
|
|
"bitwarden": {
|
|
|
|
"enable": userdata.get("bitwarden", {}).get("enable", False),
|
|
|
|
"location": userdata.get("bitwarden", {}).get(
|
|
|
|
"location", "sda1"
|
|
|
|
),
|
|
|
|
},
|
|
|
|
"gitea": {
|
|
|
|
"enable": userdata.get("gitea", {}).get("enable", False),
|
|
|
|
"location": userdata.get("gitea", {}).get("location", "sda1"),
|
|
|
|
},
|
|
|
|
"jitsi-meet": {
|
|
|
|
"enable": userdata.get("jitsi", {}).get("enable", False),
|
|
|
|
},
|
|
|
|
"nextcloud": {
|
|
|
|
"enable": userdata.get("nextcloud", {}).get("enable", True),
|
|
|
|
"location": userdata.get("nextcloud", {}).get(
|
|
|
|
"location", "sda1"
|
|
|
|
),
|
|
|
|
},
|
|
|
|
"ocserv": {
|
|
|
|
"enable": userdata.get("ocserv", {}).get("enable", False),
|
|
|
|
},
|
|
|
|
"pleroma": {
|
|
|
|
"enable": userdata.get("pleroma", {}).get("enable", False),
|
|
|
|
"location": userdata.get("pleroma", {}).get("location", "sda1"),
|
|
|
|
},
|
|
|
|
"simple-nixos-mailserver": {
|
|
|
|
"enable": True,
|
|
|
|
"location": userdata.get("email", {}).get("location", "sda1"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"volumes": userdata.get("volumes", []),
|
|
|
|
"ssh": {"rootKeys": userdata.get("ssh", {}).get("rootKeys", [])},
|
|
|
|
"stateVersion": "23.11",
|
|
|
|
}
|
|
|
|
with open("/etc/nixos/userdata.json", "w") as file:
|
|
|
|
json.dump(new_userdata, file, indent=4)
|
|
|
|
print("New userdata.json generated.")
|
|
|
|
|
|
|
|
print("Generating /etc/selfprivacy/secrets.json")
|
2023-12-31 14:49:57 +00:00
|
|
|
subprocess.check_output(
|
|
|
|
[
|
|
|
|
"mkdir",
|
2023-12-31 14:56:39 +00:00
|
|
|
"-p",
|
2023-12-31 14:49:57 +00:00
|
|
|
"/etc/selfprivacy",
|
|
|
|
]
|
|
|
|
)
|
2023-12-29 14:24:08 +00:00
|
|
|
secrets_contents = {
|
|
|
|
"databasePassword": userdata.get("databasePassword"),
|
|
|
|
"dns": {"apiKey": userdata.get("dns", {}).get("apiKey", "INVALID")},
|
|
|
|
"modules": {
|
|
|
|
"nextcloud": {
|
|
|
|
"adminPassword": userdata.get("nextcloud", {}).get(
|
|
|
|
"adminPassword", "INVALID"
|
|
|
|
),
|
|
|
|
"databasePassword": userdata.get("nextcloud", {}).get(
|
|
|
|
"databasePassword", "INVALID"
|
|
|
|
),
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
with open("/etc/selfprivacy/secrets.json", "w") as file:
|
|
|
|
json.dump(secrets_contents, file, indent=4)
|
|
|
|
os.chmod("/etc/selfprivacy/secrets.json", 0o600)
|
|
|
|
print("secrets.json generated.")
|
|
|
|
|
|
|
|
print("Building NixOS")
|
|
|
|
subprocess.check_output(
|
|
|
|
["nix", "flake", "lock", "/etc/nixos", "--update-input", "sp-modules"]
|
|
|
|
)
|
|
|
|
subprocess.check_output(
|
|
|
|
[
|
|
|
|
"nixos-rebuild",
|
|
|
|
"boot",
|
|
|
|
"--flake",
|
|
|
|
"/etc/nixos#default",
|
|
|
|
]
|
|
|
|
)
|
2023-12-31 15:29:59 +00:00
|
|
|
print("================================")
|
2023-12-29 14:24:08 +00:00
|
|
|
print(
|
|
|
|
"NixOS built. You may reboot now! Creating a notification for the app"
|
|
|
|
)
|
2023-12-31 15:29:59 +00:00
|
|
|
print("================================")
|
2023-12-29 14:24:08 +00:00
|
|
|
|
|
|
|
Jobs.add(
|
|
|
|
name="NixOS upgrade to 23.11",
|
|
|
|
type_id="migrations.migrate_to_flakes",
|
|
|
|
status=JobStatus.FINISHED,
|
|
|
|
status_text="New system built. Reboot your server to apply.",
|
|
|
|
progress=100,
|
2024-01-08 19:22:35 +00:00
|
|
|
description="Migration to the modular SelfPrivacy system",
|
2023-12-29 14:24:08 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
except Exception as error:
|
|
|
|
os.chdir(current_working_directory)
|
|
|
|
print("Error")
|
|
|
|
print(error)
|
|
|
|
Jobs.add(
|
|
|
|
name="NixOS upgrade to 23.11",
|
|
|
|
type_id="migrations.migrate_to_flakes",
|
|
|
|
status=JobStatus.ERROR,
|
|
|
|
status_text=str(error),
|
2024-01-08 19:22:35 +00:00
|
|
|
description="Migration to the modular SelfPrivacy system",
|
2023-12-29 14:24:08 +00:00
|
|
|
)
|
2023-12-31 15:29:59 +00:00
|
|
|
# Recover the old configuration if the nixos.pre-flakes exists
|
|
|
|
if os.path.exists("/etc/nixos.pre-flakes"):
|
|
|
|
print("Recovering the old configuration")
|
|
|
|
# Move the new configuration to /etc/nixos.new
|
|
|
|
subprocess.check_output(
|
|
|
|
[
|
|
|
|
"mv",
|
|
|
|
"-v",
|
|
|
|
"/etc/nixos",
|
|
|
|
"/etc/nixos.failed",
|
|
|
|
]
|
|
|
|
)
|
|
|
|
subprocess.check_output(
|
|
|
|
[
|
|
|
|
"mv",
|
|
|
|
"-v",
|
|
|
|
"/etc/nixos.pre-flakes",
|
|
|
|
"/etc/nixos",
|
|
|
|
]
|
|
|
|
)
|
|
|
|
print("Recovered the old configuration")
|